﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Microsoft.Win32;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Deployment;
using System.Windows;
using StressTesterCommon;
using StressServer;
using System.Windows.Threading;
using StressServer.ViewModel;
using System.Threading;


namespace StressServer.ViewModel
{
    public class Message
    {
        public Message(string Text, int Type)
        {
            this.Time = DateTime.Now;
            this.Text = Text;
            this.Type = Type;
        }
        private string _Text;
        public string FormattedMessage
        {
            get { return Time.Hour.ToString().PadLeft(2, '0') + ":" + Time.Minute.ToString().PadLeft(2, '0') + ":" + Time.Second.ToString().PadLeft(2, '0') + ">" + _Text; }
        }
        public string Text
        {
            get { return _Text; }
            set { _Text = value; }
        }
        private DateTime _Time;

        public DateTime Time
        {
            get { return _Time; }
            set { _Time = value; }
        }
        private int _Type; //1 == normal, 2==success, 3==failure

        public int Type
        {
            get { return _Type; }
            set { _Type = value; }
        }
    }

    public class ServerViewModel:INotifyPropertyChanged
    {
        #region NotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string info)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion

        #region Constructors
        public ServerViewModel(StressServer Server)
        {
            _Server = Server;
            Messages = new MTObservableCollection<Message>();
            Test = new TestViewModel(Server.TestConfiguration, this);
            _CommandSendMessage = new RelayCommand(ExecuteSendMessage) { IsEnabled = true };
            _CommandLoadTest = new RelayCommand(ExecuteLoadTest) { IsEnabled = true };
            _CommandNewTest = new RelayCommand(ExecuteNewTest) { IsEnabled = true };
            _CommandSaveTest = new RelayCommand(ExecuteSaveTest) { IsEnabled = true };
            _CommandConnect = new RelayCommand(ExecuteConnect) { IsEnabled = true };
            _CommandDisconnect = new RelayCommand(ExecuteDisconnect) { IsEnabled = true };
            _CommandExecuteTest = new RelayCommand(ExecuteTest) { IsEnabled = true };
            _CommandAbortTest = new RelayCommand(ExecuteAbortTest) { IsEnabled = true };
            this.Port = 1000;
            this.IsTestExecuting = false;
            _Clients = new MTObservableCollection<ClientProxyViewModel>();
            _Server.OnClientIdentified+=_Server_OnClientIdentified;
            _Server.OnClientDisconnect += _Server_OnClientDisconnect;
            _Server.OnClientDied += _Server_OnClientDied;
            _Server.OnXMLACacheCleared += _Server_OnXMLACacheCleared;
            _Server.OnWriteToFileStarted += _Server_OnWriteToFileStarted;
            _Server.OnWriteToFileCompleted += _Server_OnWriteToFileCompleted;
            AddMessage("Ready", 1);
        }







        #endregion

        #region Members
        private MTObservableCollection<Message> _Messages;

        public MTObservableCollection<Message> Messages
        {
            get { return _Messages; }
            set { _Messages = value; OnPropertyChanged("Messages"); }
        }
        private int _ClientsConfigured = 0;
        private int _TestsCompleted = 0;
        public int TestsCompleted
        {
            get { return _TestsCompleted; }
            set
            {
                _TestsCompleted = value; OnPropertyChanged("TestsCompleted");
            }
        }
        private DateTime _TestStarted;
        private DispatcherTimer _TestTimer;
        private string _TestDuration = "00:00:00";
        public string TestDuration
        {
            get { return _TestDuration; }
            set
            {
                if (value != _TestDuration) { _TestDuration = value; OnPropertyChanged("TestDuration"); }
            }
        }
        private string _Status = "Offline";
        public string Status
        {
            get { return _Status; }
            set { if (value != _Status) { _Status = value; OnPropertyChanged("Status"); } }
        }
        private int _QueryErrors = 0;

        public int QueryErrors
        {
            get { return _QueryErrors; }
            set { _QueryErrors = value; OnPropertyChanged("QueryErrors"); }
        }

        private int _QueriesCompleted = 0;

        public int QueriesCompleted
        {
            get { return _QueriesCompleted; }
            set { _QueriesCompleted = value; OnPropertyChanged("QueriesCompleted"); }
        }
        
        public int TotalNumberOfQueries
        {
            get { return _Server.TotalNumberOfQueries; }
        }
        private int _TestAbortedCount = 0;
        public bool IsTestExecutable
        {
            get
            {
                if (_IsTestAborting == false && _TestIsExecuting == false && _HasTest)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        private bool _IsTestAborting = false;
        public bool IsTestAborting
        {
            get { return _IsTestAborting; }
            set { if (value != _IsTestAborting) { _IsTestAborting = value; OnPropertyChanged("IsTestAborting"); OnPropertyChanged("IsTestExecutable"); } }
        }
        private string _Message = "";
        public string Message
        {
            get { return _Message; }
            set { if (value != _Message) { _Message = value; OnPropertyChanged("Message"); } }
        }
        private bool _HasTest = false;
        public bool HasTest
        {
            get { return _HasTest; }
            set { if (value != _HasTest) { _HasTest = value; OnPropertyChanged("HasTest"); OnPropertyChanged("IsTestExecutable"); } }
        }
        private bool _TestIsExecuting;
        public bool IsTestExecuting
        {
            get { return _TestIsExecuting; }
            set { _TestIsExecuting = value; OnPropertyChanged("IsTestExecuting"); OnPropertyChanged("IsTestNotExecuting"); OnPropertyChanged("IsTestExecutable"); }
        }
        public bool IsTestNotExecuting
        {
            get { return !_TestIsExecuting; }
        }
        private int _ConnectionTestOkCount = 0;
        private int _ConnectionTestFailedCount = 0;

        private MTObservableCollection<ClientProxyViewModel> _Clients;
        
        public MTObservableCollection<ClientProxyViewModel> Clients
        {
            get { return _Clients; }
            set { if (value != _Clients) { _Clients = value; OnPropertyChanged("Clients"); } }
        }
        private TestViewModel _Test;
        public TestViewModel Test
        {
            get { return _Test; }
            set { _Test = value; if (_Test != value) { _Test = value; OnPropertyChanged("Test"); } }
        }
        private StressServer _Server;
        public bool IsConnected
        {
            get { return !IsDisconnected; }
        }
        private bool _IsDisconnected = true;
        public bool IsDisconnected
        {
            get { return _IsDisconnected; }
            set { if (value != _IsDisconnected) { _IsDisconnected = value; OnPropertyChanged("IsDisconnected"); OnPropertyChanged("IsConnected"); } }
        }
        private int _Port;
        public int Port
        {
            get { return _Port; }
            set { if (value != _Port) { _Port = value; OnPropertyChanged("Port"); } }
        }
        #endregion
        #region Command declarations
        private ICommand _CommandSendMessage;
        public ICommand CommandSendMessage
        {
            get { return _CommandSendMessage; }
        }

        private ICommand _CommandAbortTest;
        public ICommand CommandAbortTest
        {
            get { return _CommandAbortTest; }
        }

        private ICommand _CommandExecuteTest;
        public ICommand CommandExecuteTest
        {
            get { return _CommandExecuteTest; }
        }
        private ICommand _CommandDisconnect;
        public ICommand CommandDisconnect
        {
            
            get { return _CommandDisconnect; }
        }
        private ICommand _CommandConnect;
        public ICommand CommandConnect
        {
            get {return _CommandConnect;}
        }

        private ICommand _CommandLoadTest;
        public ICommand CommandLoadTest
        {
            get { return _CommandLoadTest; }
        }

        private ICommand _CommandSaveTest;
        public ICommand CommandSaveTest
        {
            get { return _CommandSaveTest; }
        }
        private ICommand _CommandNewTest;
        public ICommand CommandNewTest
        {
            get { return _CommandNewTest; }
        }

        #endregion
        #region Command implementations

        public void ExecuteAbortTest()
        {
            this.Status = "Aborting test";
            AddMessage("Aborting test", 1);
            this.IsTestExecuting = false;
            this.IsTestAborting = true;
            foreach (ClientProxyViewModel Client in _Clients)
            {
                Client.Status = "Aborting test";
            }
            _Server.AbortTest();
        }

        public void ExecuteTest ()
        {
            this.TestsCompleted = 0;
            this.IsTestAborting = false;
            _ClientsConfigured = 0;
            AddMessage("Executing test", 1);
            this.Status = "Executing test";
            _TestTimer = new DispatcherTimer();
            _TestTimer.Tick += _TestTimer_Tick;
            _TestTimer.Interval = new TimeSpan(0, 0, 1);
            _TestStarted = DateTime.Now;
            _TestTimer.Start();
            this.QueriesCompleted = 0;
            this.QueryErrors = 0;
          
            OnPropertyChanged("TotalNumberOfQueries"); //?
            _TestAbortedCount = 0;
            _ConnectionTestOkCount = 0;
            _ConnectionTestFailedCount = 0;
            if (_Server.ValidateTest() == true)
            {
                AddMessage("Test validated", 2);
                this.IsTestExecuting = true;
                _Server.SendTestConfigToClients(); 
            }
            else
            {
                AddMessage("Failed to validate test", 3);
                this.Status = "Test validation failed";
                MessageBox.Show("Validation of test failed!");
            }
        }



        public void ExecuteDisconnect()
        {
            this.Status = "Offline";
            AddMessage("Offline", 1);
            _Server.Disconnect();
            this.IsDisconnected = true;
        }
        public void ExecuteConnect()
        {
            AddMessage("Online", 1);
            this.Status = "Online";
            _Server.Connect(Port);
            this.IsDisconnected = false;
        }

        public void ExecuteSendMessage()
        {
            if (_Message.Length > 0)
            {
                AddMessage("Sent message: " + _Message, 1);
                _Server.SendMessageToClients(_Message);
            }
            Message = "";
        }

        public void ExecuteNewTest()
        {
            _Server.TestConfiguration = new StressTesterCommon.Test();
            this.Test = new TestViewModel(_Server.TestConfiguration, this);
            Test.Name = "New test";
            OnPropertyChanged("Test");
            AddMessage("New test created", 1);
            this.HasTest = true;
        }

        public void ExecuteLoadTest()
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.DefaultExt = ".test";
            dlg.Title = "Select test file";
            dlg.Filter = "TEST files (*.test)|*.test";
            Nullable<bool> Res = dlg.ShowDialog();
            if (Res == true)
            {
                _Server.TestConfiguration.LoadFromFile(dlg.FileName);
                this.Test = new TestViewModel(_Server.TestConfiguration, this);
                OnPropertyChanged("Test");
                this.HasTest = true;
                AddMessage("Loaded " + dlg.FileName, 1);
            }
        }

        public void ExecuteSaveTest()
        {
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.DefaultExt = ".test";
                dlg.Title = "Save / Save as Test";
                dlg.CheckPathExists = true;
                dlg.CreatePrompt = false;
                dlg.Filter = "TEST files (*.test)|*.test";
                Nullable<bool> Res = dlg.ShowDialog();
                if (Res == true)
                {
                    _Server.TestConfiguration.SaveToFile(dlg.FileName);
                    AddMessage("Saved " + dlg.FileName, 1);
                }
            }
        }


        #endregion
        #region Events
        void _Server_OnXMLACacheCleared(bool result)
        {
            if (result)
            {
                AddMessage("SSAS cache cleared", 2);
            }
            else
            {
                AddMessage("Failed to clear SSAS cache", 3);
            }
        }
        void _Server_OnWriteToFileCompleted(bool Success)
        {
            if (Success)
            {
                AddMessage("Output written to file", 2);
            }
            else
            {
                AddMessage("Failed to write output to file", 3);
            }
        }
        void _Server_OnWriteToFileStarted()
        {
            AddMessage("Started writing output to file", 1);
        }
        void _TestTimer_Tick(object sender, EventArgs e)
        {
            TimeSpan Span = DateTime.Now - _TestStarted;
            string Time = Span.Hours.ToString().PadLeft(2, '0') +
                ':' + Span.Minutes.ToString().PadLeft(2, '0') +
                ':' + Span.Seconds.ToString().PadLeft(2, '0');
            this.TestDuration = Time;
        }
        void _Server_OnClientIdentified(StressClientProxy Cli)
        {
            ClientProxyViewModel Client = new ClientProxyViewModel(Cli);
            Client.OnConfiguredRecieved += Client_OnConfiguredRecieved;
            Client.OnTestConnectionRecieved += Client_OnTestConnectionRecieved;
            Client.OnTestAborted += Client_OnTestAborted;
            Client.OnQueryResultRecieved += Client_OnQueryResultRecieved;
            Client.OnTestCompleted += Client_OnTestCompleted;
            _Clients.Add(new ClientProxyViewModel(Cli));
            AddMessage("Client " + Cli.IP + " connected", 1);
        }
        void Client_OnTestCompleted(ClientProxyViewModel Client)
        {
            TestsCompleted++;
            AddMessage("Client " + Client.IP + " completed test", 2);
            
            if (TestsCompleted == Clients.Count)
            {
                AddMessage("Test completed", 2);
                this.Status = "Test completed";
                _TestTimer.Stop();
                this.IsTestExecuting = false;
               

            }           
        }
        void Client_OnQueryResultRecieved(ClientProxyViewModel Client, QueryResult Result)
        {
            if (Result.ExecuteStatus == QueryExecuteStatus.QueryFailed)
            {
                AddMessage("Query " + Result.QueryName + " on client " + Client.IP + " failed: " + Result.ErrorMessage, 3);
                this.QueryErrors++;
            }
            this.QueriesCompleted++;
        }
        void Client_OnTestAborted(ClientProxyViewModel Client)
        {
            _TestAbortedCount++;
            if (_TestAbortedCount == _Clients.Count)
            {
                AddMessage("Test aborted", 1);
                this.Status = "Test aborted";
                this.IsTestAborting = false;
                _TestTimer.Stop();
            }
        }
        void Client_OnTestConnectionRecieved(ClientProxyViewModel Client, bool Success)
        {
            if (Success)
            {
                _ConnectionTestOkCount++;
                AddMessage("Client " + Client.IP + " can connect to target", 2);
            }
            else
            {
                AddMessage("Client  " + Client.IP + " failed to connect to target", 3);
                _ConnectionTestFailedCount++;
            }
            if (_ConnectionTestOkCount == _Clients.Count)
            {
                //Prep for test
                foreach (ClientProxyViewModel C in _Clients)
                {
                    C.TotalQueryCount = this.Test.TotalQueries;
                    C.QueryErrors = 0;
                    C.QueriesCompleted = 0;
                }
                _Server.SendExecuteTestToClients();

            }
            if ((_ConnectionTestFailedCount > 0) && ((_ConnectionTestFailedCount + _ConnectionTestOkCount) == _Clients.Count))
            {
                this.Status = "Client(s) failed to validate test";
                this.IsTestExecuting = false;
                _TestTimer.Stop();
            }
        }
        void Client_OnConfiguredRecieved(ClientProxyViewModel Client, bool Success)
        {
            _ClientsConfigured++;
            if (_ClientsConfigured == _Clients.Count)
            {
                AddMessage("All clients configured with test", 1);
                AddMessage("Testing connection to target from clients", 1);
                _Server.SendTestConnectionToClients();
            }
        }
        void _Server_OnClientDisconnect(StressClientProxy Cli)
        {
            AddMessage("Client " + Cli.IP + " disconnected", 1);
            RemoveClient(Cli);
        }
        void _Server_OnClientDied(StressClientProxy Cli)
        {
            AddMessage("Client " + Cli.IP + " disconnected", 1);
            RemoveClient(Cli);
        }
        #endregion
        #region Methods
        private void AddMessage(string Message, int Type)
        {
            Message NewMessage = new Message(Message, Type);
            Messages.Insert(0, NewMessage);
            Thread.Sleep(10);
           
        }
        private void RemoveClient(StressClientProxy Cli)
        {
            if (Cli == null) { return; }
            for (int i = 0; i < Clients.Count; i++)
            {
                if (Clients[i].Id == Cli.Id)
                {
                    Clients.RemoveAt(i);
                    return;
                }
            }

        }
        
        #endregion


    }

}
